--! This file is part of the Wupper firmware distribution (https://gitlab.nikhef.nl/franss/wupper).
--! Copyright (C) 2001-2021 CERN for the benefit of the ATLAS collaboration.
--! Authors:
--!               RHabraken
--!               Frans Schreuder
--!
--!   Licensed under the Apache License, Version 2.0 (the "License");
--!   you may not use this file except in compliance with the License.
--!   You may obtain a copy of the License at
--!
--!       http://www.apache.org/licenses/LICENSE-2.0
--!
--!   Unless required by applicable law or agreed to in writing, software
--!   distributed under the License is distributed on an "AS IS" BASIS,
--!   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
--!   See the License for the specific language governing permissions and
--!   limitations under the License.


--!------------------------------------------------------------------------------
--!
--!           NIKHEF - National Institute for Subatomic Physics
--!
--!                       Electronics Department
--!
--!-----------------------------------------------------------------------------
--! @class virtex7_dma_top
--!
--!
--! @author      Andrea Borga    (andrea.borga@nikhef.nl)<br>
--!              Frans Schreuder (frans.schreuder@nikhef.nl)
--!
--!
--! @date        07/01/2015    created
--!
--! @version     1.1
--!
--! @brief
--! Top level design containing a simple application and the PCIe DMA
--! core
--!
--!
--! 11/19/2015 B. Kuschak <brian@skybox.com>
--!          Modifications for KCU105.
--!
--!
--!-----------------------------------------------------------------------------
--! @TODO
--!
--!
--! ------------------------------------------------------------------------------
--
--! @brief ieee



library ieee, UNISIM;
    use ieee.numeric_std.all;
    use UNISIM.VCOMPONENTS.all;
    use ieee.std_logic_unsigned.all;-- @suppress "Deprecated package"
    use ieee.std_logic_1164.all;
    use work.pcie_package.all;
    
entity wupper_oc_top is
    generic(
        NUMBER_OF_INTERRUPTS  : integer := 8;
        NUMBER_OF_DESCRIPTORS : integer := 5;
        CARD_TYPE             : integer := 155;
        BUILD_DATETIME        : std_logic_vector(39 downto 0) := x"0000FE71CE";
        GIT_HASH              : std_logic_vector(159 downto 0) := x"0000000000000000000000000000000000000000";
        COMMIT_DATETIME       : std_logic_vector(39 downto 0) := x"0000FE71CE";
        GIT_TAG               : std_logic_vector(127 downto 0) := x"00000000000000000000000000000000";
        GIT_COMMIT_NUMBER     : integer := 0;
        PCIE_LANES            : integer := 8;
        DATA_WIDTH            : integer := 1024;
        ENDPOINTS             : integer := 2;
        USE_VERSAL_CPM        : boolean := true);
    port (
        leds        : out    std_logic_vector(NUM_LEDS(CARD_TYPE)-1 downto 0); --! 8 status leds
        pcie_rxn    : in     std_logic_vector((ENDPOINTS*PCIE_LANES)-1 downto 0);
        pcie_rxp    : in     std_logic_vector((ENDPOINTS*PCIE_LANES)-1 downto 0);
        pcie_txn    : out    std_logic_vector((ENDPOINTS*PCIE_LANES)-1 downto 0);
        pcie_txp    : out    std_logic_vector((ENDPOINTS*PCIE_LANES)-1 downto 0); --! PCIe link lanes
        sys_clk_n   : in     std_logic_vector(ENDPOINTS-1 downto 0);
        sys_clk_p   : in     std_logic_vector(ENDPOINTS-1 downto 0); --! 100MHz PCIe reference clock
        SDA         : inout  std_logic;
        SCL         : inout  std_logic;
        sys_reset_n : in     std_logic; --PCIE PERSTn pin.
        DDR_in : in DDR_in_array_type(0 to NUM_DDR(CARD_TYPE)-1);
        DDR_out : out DDR_out_array_type(0 to NUM_DDR(CARD_TYPE)-1);
        DDR_inout : inout DDR_inout_array_type(0 to NUM_DDR(CARD_TYPE)-1);
        LPDDR_in                  : in     LPDDR_in_array_type(0 to NUM_LPDDR(CARD_TYPE)/2-1);
        LPDDR_out                 : out    LPDDR_out_array_type(0 to NUM_LPDDR(CARD_TYPE)-1);
        LPDDR_inout               : inout  LPDDR_inout_array_type(0 to NUM_LPDDR(CARD_TYPE)-1)
    );
end entity wupper_oc_top;


architecture structure of wupper_oc_top is

    signal pll_locked                          : std_logic; -- @suppress "signal pll_locked is never read"
    signal appreg_clk                          : std_logic;
    signal register_map_hk_monitor : register_map_hk_monitor_type; -- @suppress "signal register_map_hk_monitor is never written"
    signal register_map_control             : register_map_control_type; --! contains all read/write registers that control the application. The record members are described in pcie_package.vhd -- @suppress "signal register_map_control is never read"
    signal reset_soft : std_logic; -- @suppress "signal reset_soft is never read"
    signal lnk_up : std_logic_vector(1 downto 0); -- @suppress "signal lnk_up is never read"
    signal PCIE_PERSTn: std_logic;    

    signal WupperToCPM: WupperToCPM_array_type(0 to 1);
    signal CPMToWupper: CPMToWupper_array_type(0 to 1);
    --signal clk100: std_logic; -- @suppress "signal clk100 is never read"

    function USE_ULTRARAM(c: integer)return boolean is
    begin
        if c = 128 or c = 800 or c = 801 or c = 180 or c = 181 or c = 182 or c = 155 or c = 120 then
            return true;
        else
            return false;
        end if;
    end function;
    signal register_map_gen_board_info : register_map_gen_board_info_type;
    signal versal_sys_reset_n : std_logic;

begin
    g_versal: if CARD_TYPE = 180 or CARD_TYPE = 155 or CARD_TYPE = 120 generate
        PCIE_PERSTn <= versal_sys_reset_n;
    else generate
        PCIE_PERSTn <= sys_reset_n;
    end generate;

    
    hk0: entity work.housekeeping_module
        generic map (
            CARD_TYPE => CARD_TYPE,
            ENDPOINTS => ENDPOINTS,
            BLOCKSIZE => 1024,
            DATA_WIDTH => DATA_WIDTH,
            USE_VERSAL_CPM => USE_VERSAL_CPM
        )
        port map(
            SCL => SCL,
            SDA => SDA,
            appreg_clk => appreg_clk,
            i2cmux_rst => open,
            leds => leds,
            register_map_control => register_map_control,
            register_map_gen_board_info => register_map_gen_board_info,
            register_map_hk_monitor => register_map_hk_monitor,
            rst_soft => reset_soft,
            sys_reset_n => sys_reset_n,
            versal_sys_reset_n_out => versal_sys_reset_n,
            WupperToCPM => WupperToCPM,
            CPMToWupper => CPMToWupper,
            clk100_out => open,
            DDR_in => DDR_in,
            DDR_out => DDR_out,
            DDR_inout => DDR_inout,
            LPDDR_in => LPDDR_in,
            LPDDR_out => LPDDR_out,
            LPDDR_inout => LPDDR_inout
        );

    g_endpoints: for i in 0 to ENDPOINTS-1 generate
        signal ep_register_map_control             : register_map_control_type; --! contains all read/write registers that control the application. The record members are described in pcie_package.vhd
        signal register_map_control_appreg_clk : register_map_control_type; -- @suppress "signal register_map_control_appreg_clk is never read"
        signal reset_soft_appreg_clk : std_logic; -- @suppress "signal reset_soft_appreg_clk is never read"

        signal ep_reset_soft                       : std_logic;
        signal ep_reset_hard                       : std_logic;
        signal fromHostFifo_dout                   : std_logic_vector(DATA_WIDTH-1 downto 0); -- @suppress "signal fromHostFifo_dout is never read"
        signal fromHostFifo_rd_en                  : std_logic;
        signal fromHostFifo_empty                  : std_logic;
        signal fromHostFifo_rd_clk                 : std_logic;
        signal fromHostFifo_rst                    : std_logic;
        signal toHostFifo_wr_clk                   : std_logic;
        signal toHostFifo_rst                      : std_logic;
        --signal fromHostFifo_wr_clk                 : std_logic;

        signal ep_appreg_clk: std_logic;
        signal ep_pll_locked: std_logic; -- @suppress "signal ep_pll_locked is never read"

        signal toHostFifo_din : slv_array(0 to NUMBER_OF_DESCRIPTORS-2);
        signal toHostFifo_prog_full : std_logic_vector(NUMBER_OF_DESCRIPTORS-2 downto 0);
        signal toHostFifo_wr_en : std_logic_vector(NUMBER_OF_DESCRIPTORS-2 downto 0);
        signal clk250: std_logic;
        signal rst_hw : std_logic;

    --COMPONENT fh_dout_ila
    --    PORT (
    --        clk : IN STD_LOGIC;
    --        probe0 : IN STD_LOGIC_VECTOR(DATA_WIDTH-1 DOWNTO 0);
    --        probe1 : IN STD_LOGIC_VECTOR(0 DOWNTO 0);
    --        probe2 : IN STD_LOGIC_VECTOR(0 DOWNTO 0)
    --    );
    --END COMPONENT  ;

    begin

        g_ep0: if i = 0 generate
            register_map_control <= ep_register_map_control;
            reset_soft <= ep_reset_soft;
            appreg_clk <= ep_appreg_clk;
            pll_locked <= ep_pll_locked;
        end generate;

        --! Instantiation of the actual PCI express core. Please note the 40MHz
        --! clock required by the core, the 250MHz clock (fifo_rd_clk and fifo_wr_clk)
        --! are generated from sys_clk_p and _n
        pcie0: entity work.wupper
            generic map(
                NUMBER_OF_INTERRUPTS => NUMBER_OF_INTERRUPTS,
                NUMBER_OF_DESCRIPTORS => NUMBER_OF_DESCRIPTORS,
                BUILD_DATETIME => BUILD_DATETIME,
                CARD_TYPE => CARD_TYPE,
                GIT_HASH => GIT_HASH,
                COMMIT_DATETIME => COMMIT_DATETIME,
                GIT_TAG => GIT_TAG,
                GIT_COMMIT_NUMBER => GIT_COMMIT_NUMBER,
                FIRMWARE_MODE => 0,
                PCIE_ENDPOINT => i,
                PCIE_LANES => PCIE_LANES,
                DATA_WIDTH => DATA_WIDTH,
                SIMULATION => false,
                BLOCKSIZE => 1024,
                USE_ULTRARAM => USE_ULTRARAM(CARD_TYPE),
                USE_VERSAL_CPM => USE_VERSAL_CPM,
                ENABLE_XVC => false
            )
            port map(
                appreg_clk => ep_appreg_clk,
                sync_clk => ep_appreg_clk,
                flush_fifo => open,
                interrupt_call => (others => '0'),
                lnk_up => lnk_up(i),
                pcie_rxn => pcie_rxn(PCIE_LANES*i+(PCIE_LANES-1) downto PCIE_LANES*i),
                pcie_rxp => pcie_rxp(PCIE_LANES*i+(PCIE_LANES-1) downto PCIE_LANES*i),
                pcie_txn => pcie_txn(PCIE_LANES*i+(PCIE_LANES-1) downto PCIE_LANES*i),
                pcie_txp => pcie_txp(PCIE_LANES*i+(PCIE_LANES-1) downto PCIE_LANES*i),
                pll_locked => ep_pll_locked,
                register_map_control_sync => ep_register_map_control,
                register_map_control_appreg_clk => register_map_control_appreg_clk,
                register_map_gen_board_info => register_map_gen_board_info,
                register_map_hk_monitor => register_map_hk_monitor,
                wishbone_monitor => wishbone_monitor_c,
                dma_enable_out => open,
                reset_hard => ep_reset_hard,
                reset_soft => ep_reset_soft,
                reset_soft_appreg_clk => reset_soft_appreg_clk,
                reset_hw_in => rst_hw,
                sys_clk_n => sys_clk_n(i),
                sys_clk_p => sys_clk_p(i),
                sys_reset_n => PCIE_PERSTn,
                tohost_busy_out => open,
                fromHostFifo_dout => fromHostFifo_dout,
                fromHostFifo_empty => fromHostFifo_empty,
                fromHostFifo_rd_clk => fromHostFifo_rd_clk,
                fromHostFifo_rd_en => fromHostFifo_rd_en,
                fromHostFifo_rst => fromHostFifo_rst,
                toHostFifo_din => toHostFifo_din,
                toHostFifo_prog_full => toHostFifo_prog_full,
                toHostFifo_rst => toHostFifo_rst,
                toHostFifo_wr_clk => toHostFifo_wr_clk,
                toHostFifo_wr_en => toHostFifo_wr_en,
                clk250_out => clk250,
                CPMToWupper => CPMToWupper(i),
                WupperToCPM => WupperToCPM(i)
            );

        toHostFifo_wr_clk <= clk250;
        fromHostFifo_rd_clk <= clk250;
        toHostFifo_rst <= ep_reset_soft or ep_reset_hard;
        fromHostFifo_rst <= ep_reset_soft or ep_reset_hard;

        fromHostFifo_rd_en <= not fromHostFifo_empty;
        --g_ila_ep0: if i = 0 generate
        --    fhila0 : fh_dout_ila
        --        PORT MAP (
        --            clk => clk250,
        --            probe0 => fromHostFifo_dout,
        --            probe1(0) => fromHostFifo_empty,
        --            probe2(0) => fromHostFifo_rd_en
        --        );
        --end generate g_ila_ep0;

        rst_hw <= ep_reset_hard;


        g_descr: for descr in 0 to NUMBER_OF_DESCRIPTORS-2 generate

            signal cnt: std_logic_vector(63 downto 0);
            signal out_data: std_logic_vector(DATA_WIDTH-1 downto 0);
        begin


            cnt_combine : for i in 0 to (DATA_WIDTH/cnt'length)-1 generate
                out_data((cnt'length*(i+1))-1 downto cnt'length*i) <= cnt;
            end generate;

            cntProc: process(clk250)
            begin
                if rising_edge(clk250) then
                    if ep_reset_soft = '1' then
                        cnt <= (others => '0');
                    elsif toHostFifo_prog_full(descr) = '0' then
                        cnt <= cnt + 1;
                    end if;
                end if;
            end process;
            toHostFifo_wr_en(descr) <= not toHostFifo_prog_full(descr);
            toHostFifo_din(descr)(DATA_WIDTH-1 downto 0) <= out_data; -- @suppress "Incorrect array size in assignment: expected (<256>) but was (<DATA_WIDTH>)"
        end generate;

    end generate; --g_endpoints


end architecture structure ; -- of wupper_oc_top

